<?php

namespace app\components;

use app\helpers\AppHelper;
use app\models\Setting;
use yii;
use yii\data\ActiveDataProvider;
use yii\db\ActiveRecord;
use yii\db\Exception;
use yii\helpers\Html;

class BaseActiveRecord extends ActiveRecord {

    public $pageSize = 15;
    public $sortAttributes = ['id'];
    public $defaultOrder = ['id' => SORT_DESC];
    public $with = [];
    public $where = [];
    public $order;

    public $cacheId;
    public $cacheParams = [];

    public static $statusLabels = [];

    const STATUS_NO = 0;
    const STATUS_YES = 1;

    const EVENT_REFRESH_CACHE = 'refreshCache';

    public static $statusColors = [
        self::STATUS_NO => 'warning',
        self::STATUS_YES => 'info'
    ];

    public function init() {
        parent::init();
        $this->on(static::EVENT_REFRESH_CACHE, [$this, 'refreshCache']);
    }

    public function behaviors() {
        $behaviors = parent::behaviors();
        return $behaviors;
    }

    public function search($params = [], $isArray = FALSE) {
        $query = static::find();
        $query->with = $this->with;
        $query->asArray($isArray);

        if (!empty($params)) {
            foreach ($params as $attribute => $value) {
                if ($this->isAttributeSafe($attribute)) {
                    $query->andFilterWhere([$attribute => $value]);
                }
            }
        }

        if ($this->order) {
            $query->orderBy($this->order);
        }
        $query->andFilterWhere($this->where);

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
            'sort' => ['attributes' => $this->sortAttributes, 'defaultOrder' => $this->defaultOrder],
            'pagination' => $this->pageSize === FALSE ? FALSE : ['defaultPageSize' => $this->pageSize],
        ]);

        return $dataProvider;
    }

    public function getError() {
        return current($this->getFirstErrors());
    }

    public function beforeSave($insert) {
        if ($this->hasAttribute('ctime') && $insert && empty($this->getAttribute('ctime'))) {
            $this->setAttribute('ctime', time());
        }
        if ($this->hasAttribute('utime') && !$insert && empty($this->getAttribute('utime'))) {
            $this->setAttribute('utime', time());
        }
        return parent::beforeSave($insert);
    }

    /**
     * @param bool $validation
     * @param null $fields
     * @return array|bool
     * @throws Exception
     */
    public function save($validation = TRUE, $fields = NULL) {
        if (!parent::save($validation, $fields)) {
            throw new Exception($this->getError());
        }
        return self::retOK();
    }

    /**
     * @return array|false|int
     * @throws Exception
     * @throws \Exception
     * @throws \Throwable
     * @throws yii\db\StaleObjectException
     */
    public function delete() {
        if (!parent::delete()) {
            throw new Exception($this->getError());
        }
        return self::retOK();
    }


    /**
     * @param $column
     * @param $value
     * @param string $op
     * @return int
     * @throws Exception
     */
    public function updateColumn($column, $value, $op = '-') {
        if (!in_array($op, ['-', '+'])) {
            throw new Exception("Invalid op {$op}");
        }

        $table = static::tableName();
        $sql = "UPDATE {$table} SET `$column` = `$column` {$op} {$value} WHERE `id` = {$this->id}";
        return Yii::$app->db->createCommand($sql)->execute();
    }

    public function isOk($res) {
        return $res['code'] == AppHelper::SUCCESS_CODE;
    }

    public static function retErr($msg = '', $code = AppHelper::ERROR_CODE, $data = NULL) {
        if (is_array($msg)) {
            $msg = yii\helpers\Json::encode($msg);
        }
        return AppHelper::error($msg, $code, $data);
    }

    public static function retOK($data = NULL, $msg = 'ok') {
        return AppHelper::success($data, $msg);
    }

    public function _statusName($statusLabels = NULL, $field = 'status') {
        $v = $this->getAttribute($field);
        if (NULL === $statusLabels) {
            $statusLabels = static::$statusLabels;
        }
        return isset($statusLabels[$v]) ? $statusLabels[$v] : '';
    }

    public function _statusLabel($statusLabels = NULL, $field = 'status') {
        $name = $this->_statusName($statusLabels, $field);
        $style = $this->_statusName(static::$statusColors, $field);
        if (empty($style)) {
            $style = 'info';
        }
        $options = ['class' => "label label-{$style}"];
        return Html::tag('span', $name, $options);
    }

    public function _IsStatusNo($field = 'status') {
        $v = intval($this->getAttribute($field));
        return $v === intval(static::STATUS_NO);
    }

    public function getIsStatusNo() {
        $v = intval($this->getAttribute('status'));
        return $v === intval(static::STATUS_NO);
    }

    protected static function getUUId($uid = NULL) {
        while (TRUE) {
            $id = AppHelper::generateUUid(12, $uid);
            if (!static::findOne($id)) {
                return $id;
            }
        }
        return $uid;
    }

    public function cache(callable $callable, $args = NULL, $duration = 0) {
        $cacheId = $this->cacheId;
        if (!$cacheId) {
            $cacheId = get_called_class();
        }

        $cacheId = sprintf($cacheId, $args);
        $data = Yii::$app->cache->get($cacheId);
        if (!$data || FALSE === $duration) {
            $data = call_user_func($callable);
            Yii::$app->cache->set($cacheId, $data, $duration);
        }
        return $data;
    }

    public function afterSave($insert, $changedAttributes) {
        $event = new yii\base\Event();
        $this->trigger(self::EVENT_REFRESH_CACHE, $event);

        parent::afterSave($insert, $changedAttributes);
    }

    public function refreshCache() {
        if (!$this->cacheId) {
            $this->cacheId = get_called_class();
        }

        if (empty($this->cacheParams)) {
            \Yii::$app->cache->delete($this->cacheId);
            return;
        }
        foreach ($this->cacheParams as $args) {
            $cacheId = sprintf($this->cacheId, $args);
            \Yii::$app->cache->delete($cacheId);
        }
    }
}